#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kallsyms.h> 
#include <linux/syscalls.h>
#include <linux/vmalloc.h>
#include <asm/unistd.h>
#include <asm/ptrace.h> 

int (*my_set_memory_ro)(unsigned long addr, int numpages);
int (*my_set_memory_rw)(unsigned long addr, int numpages);

struct vm_struct *(*my_find_vm_area)(const void *addr);

static unsigned long *__sys_call_table;

typedef long (*syscall_fn_t)(const struct pt_regs *regs);

#ifndef __NR_mkdirat
#define __NR_mkdirat 34
#endif

//用于保存原始的 mkdir 系统调用
static syscall_fn_t orig_mkdir;

asmlinkage long mkdir_hook(const struct pt_regs *regs)
{
    printk("hook mkdir sys_call\n");

    // return orig_mkdir(regs);

    return 0;
}

static unsigned long addr;

static int __init lkm_init(void)
{
    struct vm_struct *area;

    my_set_memory_ro = (void *)kallsyms_lookup_name("set_memory_ro");
    my_set_memory_rw = (void *)kallsyms_lookup_name("set_memory_rw");

    my_find_vm_area = (void *)kallsyms_lookup_name("find_vm_area");

    __sys_call_table = (unsigned long *)kallsyms_lookup_name("sys_call_table");

    printk("__sys_call_table = %lx\n", __sys_call_table);
    
    //保存原始的系统调用：mkdir
	orig_mkdir = (syscall_fn_t)__sys_call_table[__NR_mkdirat];

    addr = (unsigned long)(__sys_call_table + __NR_mkdirat);

    addr &= PAGE_MASK;

    area = my_find_vm_area((void *)addr);
    if(!area){
        printk("no find vm area\n");
        return -1;
    }

    area->flags |= VM_ALLOC;
    printk("area->addr = %p, area->size = %lx\n", area->addr, area->size);    

	//hook 系统调用表表项：sys_call_table[__NR_mkdirat]
    my_set_memory_rw(addr, 1);
    __sys_call_table[__NR_mkdirat] = (unsigned long)mkdir_hook;
    my_set_memory_ro(addr, 1);

    printk("lkm_init\n");

	return 0;
}

static void __exit lkm_exit(void)
{
	//模块卸载时恢复原来的mkdir系统调用
	my_set_memory_rw(addr, 1);
    __sys_call_table[__NR_mkdirat] = (unsigned long)orig_mkdir;
    my_set_memory_ro(addr, 1);

    printk("lkm_exit\n");
}

module_init(lkm_init);
module_exit(lkm_exit);

MODULE_LICENSE("GPL");